/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package mainclasses;

import ruter.map.AnonymousMapComponent;
import ruter.map.Map;
import ruter.map.MapComponent;
import ruter.visual.DensityDrawer;
import ruter.visual.ImageIOUtility;
import ruter.visual.Painter;
import ruter.visual.PathRecorder;
import ruter.visual.RoundBall;
import ruter.simulator.Simulation;
import ruter.simulator.Updatable;
import ruter.simulator.Vehicle;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.IOException;
import java.util.Random;

import components.RobotPilot;

import ruter.network.AdhocNetwork;
import ruter.network.UnavailableInformationException;
import visualkey.KCanvas;

/**
 *
 * @author camilo
 */
public class DensityAndPathRecorder extends KCanvas implements KeyListener, Updatable {

	public static final int[] MAP_DIMENSION = {10000, 10000};
	public static final int NODE_RADIUS = 90;
	public static final int NUMBER_OF_ROBOTS = 15;
	public static final long FRAME_TIME = Simulation.ACCURACY_HIGH;
	public static final double MAX_PPM = 20;
	public static final double MIN_PPM = 0.0625;

	public static double ppm = 0.5; // Pixels per meter

	private Map map;
	private AnonymousMapComponent target;
	private DensityDrawer density;
	private AdhocNetwork network;
	private Simulation simulation;
	private boolean findOrNot = false, allFind = false;
	private int robotsHaveFoundTarget;

	public static void main(String args[]) {
		DensityAndPathRecorder test = new DensityAndPathRecorder();
		test.setDefaultCloseOperation(test.EXIT_ON_CLOSE);
		test.setVisible(true);
	}

	public DensityAndPathRecorder() {
		super(new Dimension((int)(MAP_DIMENSION[0] * ppm + 10), (int)(MAP_DIMENSION[1] * ppm + 20)));
		createMainInstances();
		addRobots();
		addKeyListener(this);
		simulation.start();
		updateScreen(false);
	}

	public void createMainInstances() {

		// Map
		map = new Map(MAP_DIMENSION[0], MAP_DIMENSION[1], 20);

		// Network
		network = new AdhocNetwork(NODE_RADIUS);

		// Simulation
		simulation = new Simulation(this, map, network, FRAME_TIME);
		simulation.addUpdatableElementToThread(this);
		simulation.setPaintingSpeed(1000);

		// Density graph
		density = new DensityDrawer(map, 1/262144d);
		density.useLogaritmicScale(true);
		simulation.addUpdatableElementToThread(density);
	}

	private void addRobots() {
		Random random = new Random();
		target = new AnonymousMapComponent(map.getXBounds()*0.1, map.getYBounds()*0.1);
		int mod = (int) Math.ceil(Math.sqrt(NUMBER_OF_ROBOTS));
		int refX = (int) (map.getXBounds()/2 - mod*3);
		int refY = (int) (map.getYBounds()/2 - mod*3);
		for (int i = 0; i < NUMBER_OF_ROBOTS; i++) {
			int x = i % mod;
			int y = (int) (i/(double)mod);
			Vehicle v = new Vehicle(refX + x*mod, refY + y*mod, 2, 1, 1, 180);
			RobotPilot pilot = new RobotPilot(map, target, FRAME_TIME/1000d,simulation);
			pilot.setUseRules(false);
			v.putDriver(pilot);
			v.putMap(map);
			//			v.addAccesory(new PathRecorder(100000));
			//			v.addAccesory(new RoundBall(network.getMinimumDistanceConnection()));
			network.addNode(pilot);
			simulation.addVehicle(v, new Color(random.nextInt()));
		}
	}

	@Override
	protected void paintCanvas(Graphics g) {
		if(density != null)
			density.draw(g, ppm);
		simulation.repaint(g, ppm);
	}

	private int numberOfRobotsHaveFoundTarget(){
		int robotsFoundTarget = 0;
		for (int i = 0; i < simulation.getVehicles().size(); i++) {
			RobotPilot robot = (RobotPilot) simulation.getVehicles().get(i).getDriver();
			if(robot.getTimeToFind()!=0){
				if(!findOrNot) {
					findOrNot = true;
				}
				robotsFoundTarget ++;
			}
		}
		if(simulation.getVehicles().size() == robotsFoundTarget && !allFind) {
			allFind = true;
		}
		return robotsFoundTarget;
	}

	public void updateScreen(boolean requestScreenAdjust) {
		super.setTitle("Ruter - PPM: " + ppm + " - speed: " + (simulation.getSimulationSpeed() > 1 ? "1/" + (int) simulation.getSimulationSpeed() + "x" : (int) (1 / simulation.getSimulationSpeed()) + "x"));
		if (requestScreenAdjust) {
			modifyCanvasSize(new Dimension((int)(MAP_DIMENSION[0] * ppm) + 10, (int)(MAP_DIMENSION[1] * ppm) + 20));
		}
	}

	@Override
	public void keyPressed(KeyEvent e) {

		switch (e.getKeyCode()) {
		case KeyEvent.VK_UP:
			if (ppm < MAX_PPM) {
				ppm *= 1.125;
				updateScreen(true);
				repaint();
			}
			break;
		case KeyEvent.VK_DOWN:
			if (ppm > MIN_PPM) {
				ppm /= 1.125;
				updateScreen(true);
				repaint();
			}
			break;
		case KeyEvent.VK_RIGHT:
			simulation.increaseSimulationSpeed();
			updateScreen(false);
			break;
		case KeyEvent.VK_LEFT:
			simulation.decreaseSimulationSpeed();
			updateScreen(false);
			break;
		}

		switch (e.getKeyChar()) {

		case 'p':
			simulation.pauseSimulation();
			break;

		case 'r':
			simulation.resumeSimulation();
			break;

		case 's':
			simulation.stopSimulation();
			break;

		case 't':
			System.out.println("Time: "+simulation.getRealTime());
			break;
		}
	}

	@Override
	public void keyTyped(KeyEvent e) {}

	@Override
	public void keyReleased(KeyEvent e) {}

	@Override
	public void update(Simulation simulation) {

		// Pause at certain point
		//		if(simulation.getRealTime() == 420000 || simulation.getRealTime() == 840000) {
		//			simulation.pauseSimulation();
		//		}

		// Save image when a robot finds the target
		if(density != null) {
			int n = numberOfRobotsHaveFoundTarget();
			if(n != robotsHaveFoundTarget) {
				robotsHaveFoundTarget = n;
				ImageIOUtility image = new ImageIOUtility((int)(map.getXBounds()*ppm), (int)(map.getYBounds()*ppm));
				density.draw(image.getGraphics(), ppm);
				simulation.repaint(image.getGraphics(), ppm);
				image.getGraphics().setColor(Color.blue);
				Painter.paintSpotRepresetationOfMapComponent(image.getGraphics(), target, ppm, 5);
				try {
					image.saveImage(new File("/Users/camilo/Desktop/simulation/evolution/density"+numberOfRobotsHaveFoundTarget()), "PNG");
				} catch (IOException e) {
					System.out.println("Error al grabar imagen");
				}
			}
		}
	}

	@Override
	public void update(double x, double y) {}

}
